/**
  ******************************************************************************
  * @file    usbd_msc_core.c
  * @author  MCD Application Team
  * @version V1.1.0
  * @date    19-March-2012
  * @brief   This file provides all the MSC core functions.
  *
  * @verbatim
  *
  *          ===================================================================
  *                                MSC Class  Description
  *          ===================================================================
  *           This module manages the MSC class V1.0 following the "Universal
  *           Serial Bus Mass Storage Class (MSC) Bulk-Only Transport (BOT) Version 1.0
  *           Sep. 31, 1999".
  *           This driver implements the following aspects of the specification:
  *             - Bulk-Only Transport protocol
  *             - Subclass : SCSI transparent command set (ref. SCSI Primary Commands - 3 (SPC-3))
  *
  *  @endverbatim
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2012 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "usbd_msc_mem.h"
#include "usbd_msc_core.h"
#include "usbd_msc_bot.h"
#include "usbd_req.h"


/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @{
  */


/** @defgroup MSC_CORE
  * @brief Mass storage core module
  * @{
  */

/** @defgroup MSC_CORE_Private_TypesDefinitions
  * @{
  */
/**
  * @}
  */


/** @defgroup MSC_CORE_Private_Defines
  * @{
  */

/**
  * @}
  */


/** @defgroup MSC_CORE_Private_Macros
  * @{
  */
/**
  * @}
  */


/** @defgroup MSC_CORE_Private_FunctionPrototypes
  * @{
  */
uint8_t  USBD_MSC_Init(void  *pdev,
                       uint8_t cfgidx);

uint8_t  USBD_MSC_DeInit(void  *pdev,
                         uint8_t cfgidx);

uint8_t  USBD_MSC_Setup(void  *pdev,
                        USB_SETUP_REQ *req);

uint8_t  USBD_MSC_DataIn(void  *pdev,
                         uint8_t epnum);


uint8_t  USBD_MSC_DataOut(void  *pdev,
                          uint8_t epnum);

uint8_t  *USBD_MSC_GetCfgDesc(uint8_t speed,
                              uint16_t *length);

#ifdef USB_OTG_HS_CORE
uint8_t  *USBD_MSC_GetOtherCfgDesc(uint8_t speed,
                                   uint16_t *length);
#endif


uint8_t USBD_MSC_CfgDesc[USB_MSC_CONFIG_DESC_SIZ];




/**
  * @}
  */


/** @defgroup MSC_CORE_Private_Variables
  * @{
  */


USBD_Class_cb_TypeDef  USBD_MSC_cb =
{
    USBD_MSC_Init,
    USBD_MSC_DeInit,
    USBD_MSC_Setup,
    NULL, /*EP0_TxSent*/
    NULL, /*EP0_RxReady*/
    USBD_MSC_DataIn,
    USBD_MSC_DataOut,
    NULL, /*SOF */
    NULL,
    NULL,
    USBD_MSC_GetCfgDesc,
#ifdef USB_OTG_HS_CORE
    USBD_MSC_GetOtherCfgDesc,
#endif
};

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
/* USB Mass storage device Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
__ALIGN_BEGIN uint8_t USBD_MSC_CfgDesc[USB_MSC_CONFIG_DESC_SIZ] __ALIGN_END =
{

    0x09,   /* bLength: Configuation Descriptor size */
    USB_DESC_TYPE_CONFIGURATION,   /* bDescriptorType: Configuration */
    USB_MSC_CONFIG_DESC_SIZ,

    0x00,
    0x01,   /* bNumInterfaces: 1 interface */
    0x01,   /* bConfigurationValue: */
    0x04,   /* iConfiguration: */
    0xC0,   /* bmAttributes: */
    0x32,   /* MaxPower 100 mA */

    /********************  Mass Storage interface ********************/
    0x09,   /* bLength: Interface Descriptor size */
    0x04,   /* bDescriptorType: */
    0x00,   /* bInterfaceNumber: Number of Interface */
    0x00,   /* bAlternateSetting: Alternate setting */
    0x02,   /* bNumEndpoints*/
    0x08,   /* bInterfaceClass: MSC Class */
    0x06,   /* bInterfaceSubClass : SCSI transparent*/
    0x50,   /* nInterfaceProtocol */
    0x05,          /* iInterface: */
    /********************  Mass Storage Endpoints ********************/
    0x07,   /*Endpoint descriptor length = 7*/
    0x05,   /*Endpoint descriptor type */
    MSC_IN_EP,   /*Endpoint address (IN, address 1) */
    0x02,   /*Bulk endpoint type */
    LOBYTE(MSC_MAX_PACKET),
    HIBYTE(MSC_MAX_PACKET),
    0x00,   /*Polling interval in milliseconds */

    0x07,   /*Endpoint descriptor length = 7 */
    0x05,   /*Endpoint descriptor type */
    MSC_OUT_EP,   /*Endpoint address (OUT, address 1) */
    0x02,   /*Bulk endpoint type */
    LOBYTE(MSC_MAX_PACKET),
    HIBYTE(MSC_MAX_PACKET),
    0x00     /*Polling interval in milliseconds*/
};
#ifdef USB_OTG_HS_CORE
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t USBD_MSC_OtherCfgDesc[USB_MSC_CONFIG_DESC_SIZ] __ALIGN_END =
{

    0x09,   /* bLength: Configuation Descriptor size */
    USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION,
    USB_MSC_CONFIG_DESC_SIZ,

    0x00,
    0x01,   /* bNumInterfaces: 1 interface */
    0x01,   /* bConfigurationValue: */
    0x04,   /* iConfiguration: */
    0xC0,   /* bmAttributes: */
    0x32,   /* MaxPower 100 mA */

    /********************  Mass Storage interface ********************/
    0x09,   /* bLength: Interface Descriptor size */
    0x04,   /* bDescriptorType: */
    0x00,   /* bInterfaceNumber: Number of Interface */
    0x00,   /* bAlternateSetting: Alternate setting */
    0x02,   /* bNumEndpoints*/
    0x08,   /* bInterfaceClass: MSC Class */
    0x06,   /* bInterfaceSubClass : SCSI transparent command set*/
    0x50,   /* nInterfaceProtocol */
    0x05,          /* iInterface: */
    /********************  Mass Storage Endpoints ********************/
    0x07,   /*Endpoint descriptor length = 7*/
    0x05,   /*Endpoint descriptor type */
    MSC_IN_EP,   /*Endpoint address (IN, address 1) */
    0x02,   /*Bulk endpoint type */
    0x40,
    0x00,
    0x00,   /*Polling interval in milliseconds */

    0x07,   /*Endpoint descriptor length = 7 */
    0x05,   /*Endpoint descriptor type */
    MSC_OUT_EP,   /*Endpoint address (OUT, address 1) */
    0x02,   /*Bulk endpoint type */
    0x40,
    0x00,
    0x00     /*Polling interval in milliseconds*/
};
#endif

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN static uint8_t  USBD_MSC_MaxLun  __ALIGN_END = 0;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN static uint8_t  USBD_MSC_AltSet  __ALIGN_END = 0;

/**
  * @}
  */


/** @defgroup MSC_CORE_Private_Functions
  * @{
  */

/**
* @brief  USBD_MSC_Init
*         Initialize  the mass storage configuration
* @param  pdev: device instance
* @param  cfgidx: configuration index
* @retval status
*/
uint8_t  USBD_MSC_Init(void  *pdev,
                       uint8_t cfgidx)
{
    USBD_MSC_DeInit(pdev, cfgidx);
    /* Open EP IN */
    DCD_EP_Open(pdev,
                MSC_IN_EP,
                MSC_EPIN_SIZE,
                USB_OTG_EP_BULK);
    /* Open EP OUT */
    DCD_EP_Open(pdev,
                MSC_OUT_EP,
                MSC_EPOUT_SIZE,
                USB_OTG_EP_BULK);
    /* Init the BOT  layer */
    MSC_BOT_Init(pdev);
    return USBD_OK;
}

/**
* @brief  USBD_MSC_DeInit
*         DeInitilaize  the mass storage configuration
* @param  pdev: device instance
* @param  cfgidx: configuration index
* @retval status
*/
uint8_t  USBD_MSC_DeInit(void  *pdev,
                         uint8_t cfgidx)
{
    /* Close MSC EPs */
    DCD_EP_Close(pdev, MSC_IN_EP);
    DCD_EP_Close(pdev, MSC_OUT_EP);
    /* Un Init the BOT layer */
    MSC_BOT_DeInit(pdev);
    return USBD_OK;
}
/**
* @brief  USBD_MSC_Setup
*         Handle the MSC specific requests
* @param  pdev: device instance
* @param  req: USB request
* @retval status
*/
uint8_t  USBD_MSC_Setup(void  *pdev, USB_SETUP_REQ *req)
{
    switch(req->bmRequest & USB_REQ_TYPE_MASK)
    {
        /* Class request */
        case USB_REQ_TYPE_CLASS :
            switch(req->bRequest)
            {
                case BOT_GET_MAX_LUN :
                    if((req->wValue  == 0) &&
                            (req->wLength == 1) &&
                            ((req->bmRequest & 0x80) == 0x80))
                    {
                        USBD_MSC_MaxLun = USBD_STORAGE_fops->GetMaxLun();

                        if(USBD_MSC_MaxLun > 0)
                        {
                            USBD_CtlSendData(pdev,
                                             &USBD_MSC_MaxLun,
                                             1);
                        }
                        else
                        {
                            USBD_CtlError(pdev, req);
                            return USBD_FAIL;
                        }
                    }
                    else
                    {
                        USBD_CtlError(pdev, req);
                        return USBD_FAIL;
                    }

                    break;

                case BOT_RESET :
                    if((req->wValue  == 0) &&
                            (req->wLength == 0) &&
                            ((req->bmRequest & 0x80) != 0x80))
                    {
                        MSC_BOT_Reset(pdev);
                    }
                    else
                    {
                        USBD_CtlError(pdev, req);
                        return USBD_FAIL;
                    }

                    break;

                default:
                    USBD_CtlError(pdev, req);
                    return USBD_FAIL;
            }

            break;

        /* Interface & Endpoint request */
        case USB_REQ_TYPE_STANDARD:
            switch(req->bRequest)
            {
                case USB_REQ_GET_INTERFACE :
                    USBD_CtlSendData(pdev,
                                     &USBD_MSC_AltSet,
                                     1);
                    break;

                case USB_REQ_SET_INTERFACE :
                    USBD_MSC_AltSet = (uint8_t)(req->wValue);
                    break;

                case USB_REQ_CLEAR_FEATURE:
                    /* Flush the FIFO and Clear the stall status */
                    DCD_EP_Flush(pdev, (uint8_t)req->wIndex);
                    /* Re-activate the EP */
                    DCD_EP_Close(pdev, (uint8_t)req->wIndex);

                    if((((uint8_t)req->wIndex) & 0x80) == 0x80)
                    {
                        DCD_EP_Open(pdev,
                                    ((uint8_t)req->wIndex),
                                    MSC_EPIN_SIZE,
                                    USB_OTG_EP_BULK);
                    }
                    else
                    {
                        DCD_EP_Open(pdev,
                                    ((uint8_t)req->wIndex),
                                    MSC_EPOUT_SIZE,
                                    USB_OTG_EP_BULK);
                    }

                    /* Handle BOT error */
                    MSC_BOT_CplClrFeature(pdev, (uint8_t)req->wIndex);
                    break;
            }

            break;

        default:
            break;
    }

    return USBD_OK;
}

/**
* @brief  USBD_MSC_DataIn
*         handle data IN Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t  USBD_MSC_DataIn(void  *pdev,
                         uint8_t epnum)
{
    MSC_BOT_DataIn(pdev, epnum);
    return USBD_OK;
}

/**
* @brief  USBD_MSC_DataOut
*         handle data OUT Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t  USBD_MSC_DataOut(void  *pdev,
                          uint8_t epnum)
{
    MSC_BOT_DataOut(pdev, epnum);
    return USBD_OK;
}

/**
* @brief  USBD_MSC_GetCfgDesc
*         return configuration descriptor
* @param  speed : current device speed
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t  *USBD_MSC_GetCfgDesc(uint8_t speed, uint16_t *length)
{
    *length = sizeof(USBD_MSC_CfgDesc);
    return USBD_MSC_CfgDesc;
}

/**
* @brief  USBD_MSC_GetOtherCfgDesc
*         return other speed configuration descriptor
* @param  speed : current device speed
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
#ifdef USB_OTG_HS_CORE
uint8_t  *USBD_MSC_GetOtherCfgDesc(uint8_t speed,
                                   uint16_t *length)
{
    *length = sizeof(USBD_MSC_OtherCfgDesc);
    return USBD_MSC_OtherCfgDesc;
}
#endif
/**
  * @}
  */


/**
  * @}
  */


/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
